CSS 如何实现羽化效果?
最近碰到这样一个问题,在一张封面上直接显示书名,可能会存在书名看不太清楚的情况(容易受到背景干扰),如下
为了解决这个问题,设计师提了一个“究极”方案,将书名背后的图片模糊一下,这个在 CSS 中很好实现,仅需backdrop-filter
即可
.name{
backdrop-filter: blur(10px);
}
当然,现在模糊是模糊了,但是边缘过于“断层”,导致书名和封面有些“格格不入”,效果如下
如果能够将边缘羽化一下,虚化边缘效果,就可以很好地将书名融入到背景当中
羽化是photoshop术语,羽化原理是令选区内外衔接部分虚化,起到渐变的作用从而达到自然衔接的效果,是ps及其其它版本中的处理图片的重要工具
这是设计最终羽化后的效果,既能保证文字清晰可见,又能避免太过突兀,如下
设计师实现这个很简单,用橡皮擦擦一下就可以了,那么 CSS 呢?下面来探讨一下 CSS 如何实现羽化的效果。
一、羽化的原理
羽化其实就是将边缘变得模糊,在 CSS 中其实就是创建一个边缘模糊的遮罩(mask),也就是需要一种半透明的渐变。
关于遮罩,这里简单介绍一下,基本语法很简单,和background的语法基本一致
.content{
-webkit-mask: '遮罩图片' ;
}
这里的遮罩图片和背景的使用方式基本一致,可以是PNG图片、SVG图片、也可以是渐变绘制的图片,同时也支持多图片叠加。
遮罩的原理很简单,最终效果只显示不透明的部分,透明部分将不可见,半透明类推
先举个圆形的例子,这个比较简单,因为可以直接通过径向渐变创建
比如,下面有一个头像,现在直接放在背景上非常突兀
我们可以将这个头像通过径向渐变绘制出了一个从不透明到透明的遮罩,达到和背景融合的效果
.head{
-webkit-mask: radial-gradient(closest-side circle,#000 60%, transparent 100%);
}
原理是这样的
最后效果如下
当然这是圆形的,如果是矩形的呢?
二、矩形的羽化原理
根据上面的分析,如果希望羽化矩形边缘,需要创建这一个遮罩
那么问题来了,如何创建这一个边缘模糊的矩形呢?貌似没办法直接通过渐变来实现,而且还需要是尺寸自适应的(自动跟随容器尺寸)
如果单纯看这样一个矩形,还是很容易实现的,通过box-shadow
即可实现
.shadow{
width: 200px;
height: 200px;
background:black;
border-radius:10px;
box-shadow:0 0 5px black, 0 0 10px black, 0 0 15px black
}
根据需求,**可以多叠加几层box-shadow
**,这里叠加了3层,效果如下
但是问题又来了,这样一个 dom 无法直接用作mask-image
,那如何处理呢?
三、通过 SVG foreignObject 转换成图片
上面提到,通过 CSS 阴影可以很轻松的实现我们说需要的效果,但可惜现在还是 dom
阶段,所以需要将这个 dom 转换成图像。
在这里,需要借助 SVG
中的foreignObject[1]元素,通过这个元素,可以将 HTML
嵌入到SVG
中,轻松实现 dom
转图片的效果
有兴趣的可以参考之前这几篇文章
CSS、SVG、Canvas对特殊字体的绘制与导出 CSS & SVG foreignObject 实现文字镂空波浪动画
原理如下
回到这里,我们仅需要将上面的结果放到foreignObject
元素中,由于需要自适应尺寸,这里的body
宽高都是100%
,如下
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="100%" height="100%">
<body class="wrap" xmlns="http://www.w3.org/1999/xhtml">
<style>
.wrap{
box-sizing: border-box;
margin: 0;
height: 100%;
padding: 10px;
}
.shadow{
height: 100%;
background:black;
border-radius:10px;
box-shadow:0 0 5px black, 0 0 10px black, 0 0 15px black
}
</style>
<div class="shadow"></div>
</body>
</foreignObject>
</svg>
这样就得到了一个宽高自适应的SVG
图像,无论宽高怎么变化,都是撑满的
别看这么多标签,这其实已经是一张图片,可以直接使用,接下来看看如何运用
四、矩形的羽化
其实上面得到的 SVG
可以直接当成一个图片资源,正常使用了,就像这样
.name{
-webkit-mask: url('./fearher.svg')
}
不过,也可以将这个SVG
图片转换成内联形式,减少资源依赖,转换后仍然保持自适应特性
这里推荐张鑫旭老师的SVG在线压缩合并工具 [2]
转换后就是这个样子
.name{
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3CforeignObject width='100%25' height='100%25'%3E%3Cbody class='wrap' xmlns='http://www.w3.org/1999/xhtml'%3E%3Cstyle%3E.wrap%7Bbox-sizing:border-box;margin:0;height:100%25;padding:10px%7D.shadow%7Bheight:100%25;background:%23000;border-radius:10px;box-shadow:0 0 5px %23000,0 0 10px %23000,0 0 15px %23000%7D%3C/style%3E%3Cdiv class='shadow'/%3E%3C/body%3E%3C/foreignObject%3E%3C/svg%3E")
}
效果如下
而且由于尺寸是动态的,换个书名也能完美适应
最后再来对比一下,下面哪个一眼看上去效果最好?
完整代码可以查看以下任意链接
CSS feather (juejin.cn)[3] CSS feather (codepen.io)[4] CSS feather (runjs.work)[5]
五、总结一下
以上就是本文全部内容了,一个还不错的绘制小技巧,最后来回顾一下一些实现要点
羽化其实就是将边缘变得模糊,在 CSS 中其实就是创建一个边缘模糊的遮罩,也就是需要一种半透明的渐变。 圆形的边缘很好羽化,因为径向渐变可以直接绘制 矩形的边缘就稍微复杂点,因为不能直接通过渐变绘制 边缘模糊在 CSS 中很好实现,用 box-shadow 就行了 可以通过 SVG foreignObject 将 dom 转换成图片 SVG 转换成内联形式,好处是减少资源依赖,转换后仍然保持自适应特性
当然本文最重要的一点是,如何在 HTML 严重受限的背景上绘制一些常见的图形,以后碰到类似的需求也可以朝这个方向去考虑,最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤
参考资料
foreignObject: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject
[2]SVG在线压缩合并工具 : https://www.zhangxinxu.com/sp/svgo/
[3]CSS feather (juejin.cn): https://code.juejin.cn/pen/7175466278134480908
[4]CSS feather (codepen.io): https://codepen.io/xboxyan/pen/ZERZrQw
[5]CSS feather (runjs.work): https://runjs.work/projects/51eece3c132040f2